#include <54func.h>
int main(int argc,char *argv[])
{
    // ./6_azhen_reconnect 192.168.187.128 1234
    ARGS_CHECK(argc,3);

    //创建套接字
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    ERROR_CHECK(sockfd,-1,"socket");

    //初始化
    struct sockaddr_in serveraddr;
    serveraddr.sin_family = AF_INET;//IPv4
    serveraddr.sin_port = htons(atoi(argv[2]));
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);//将点分十进制转换为网络地址
    
    //在TCP协议中，当一个连接正常关闭后与，该连接会进入TIME_WAIT状态，以确保所有数据包都已经被正常处理，在TIME_WAIT状态期间，该端口不能被立即重新使用
    int reuse = 1;//1->运行该端口重用 0->不允许重用
    int ret = setsockopt(sockfd,SOL_SOCKET,
                         SO_REUSEADDR,&reuse,sizeof(reuse));
    ERROR_CHECK(ret,-1,"setsockopt");
    //SO_REUSEADDR：允许一个新的套接字绑定到一个本地地址和端口，即使该端口已经被另一个处于TIME_WAIT状态的套接字占用
    //在高并发服务器中非常有用，因为它需要频繁地启用和停止监听特定端口的服务

    //将socket与ip以及端口绑定
    ret = bind(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr));
    ERROR_CHECK(ret,-1,"bind");

    //listen：使socket变为被动的，使其能够接收来自客户端的连接请求
    listen(sockfd,50);

    //监听：客户端终止以后，服务端不重质，还要能接收新连接（accept）
    //因此将监听集合设计为两种情况：1）没有阿强，阿珍监听sockfd   2）有阿强，阿珍监听netfd和stdin
    char buf[4096] = {0};
    //监听集合的两种状态，要么监听sockfd，要么监听stdin和netfd
    fd_set monitorSet;//监听集合
    fd_set readySet;  //就绪集合
    FD_ZERO(&monitorSet);
    FD_SET(sockfd,&monitorSet);//首先先监听aqiang，看看aqiang是否已经连接
    int netfd = -1;//netfd为-1时，阿强不存在
    while(1)
    {
        memcpy(&readySet,&monitorSet,sizeof(monitorSet));//将监听集合拷贝到就绪集合，阿强准备就绪
        select(20,&readySet,NULL,NULL,NULL);//该情况可能存在sockfd最大或者netfd最大，因此直接把这个范围取到20
        
        //监听集合设计：
        //没有阿强：阿珍监听阿强
        //  有强：阿珍监听netfd和stdin

        if(FD_ISSET(sockfd,&readySet))//第一种情况，肯定是没有阿强，阿珍先监听
        {
            //aqiang准备就绪
            netfd = accept(sockfd,NULL,NULL);//从全连接队列中，取出连接
            printf("aqiang is connect!\n");
            FD_CLR(sockfd,&monitorSet);//aqiang已经在，将其从就绪集合中删除
            //接下来只需要监听，stdin or entfd
            FD_SET(STDIN_FILENO,&monitorSet);
            FD_SET(netfd,&monitorSet);
        }

        //aqiang已经连接，那么就需要去监听stdin和socket
        if(FD_ISSET(netfd,&readySet))
        {
            //从socket中读取数据
            bzero(buf,sizeof(buf));
            int ret = recv(netfd,buf,sizeof(buf),0);
            if(ret == 0)//socket的写端关闭
            {
                //aqiang断开连接，那么就需要再将aqiang加入到监听集合中，进行监听
                FD_SET(sockfd,&monitorSet);//重新等待新的aqiang
                //aqiang断开，azhen不能发消息，不能读取消息，将stdin 以及 netfd 从监听集合移除
                FD_CLR(STDIN_FILENO,&monitorSet);
                FD_CLR(netfd,&monitorSet);
                close(netfd);//关闭文件描述符，防开的太多
                netfd = -1;
                printf("aqiang断开连接！\n");
                continue;
            }
            printf("aqiang：%s\n",buf);//若是阿强没有断开，正常输出
        }
        if(FD_ISSET(STDIN_FILENO,&readySet))
        {
            //读标准stdin的数据
            bzero(buf,sizeof(buf));
            ssize_t sret = read(STDIN_FILENO,buf,sizeof(buf));
            if(sret == 0)//若是是azhen自己断开，则告知aqiang
            {
                //azhen想踢走aqiang
                FD_SET(sockfd,&monitorSet);
                FD_CLR(STDIN_FILENO,&monitorSet);
                FD_CLR(netfd,&monitorSet);
                close(netfd);
                netfd = -1;
                printf("azhen断开连接！\n");
                continue;
            }
            send(netfd,buf,strlen(buf),0);//把读取到的数据写入到socket中
        }
    }
    return 0;
}
